﻿using System.Collections.Generic;
using System.Xml.Linq;
using IcicleFramework.Entities;
using IcicleFramework.GameServices;
using Microsoft.Xna.Framework;
using Newtonsoft.Json;

namespace IcicleFramework.Components.Actions
{
    public class ActionComponent : BaseComponent, IActionComponent
    {
        #region Member Variables

        [JsonProperty]
        protected Dictionary<string, IAction> actions;

        protected List<string> actionsToRemoveNextFrame; 

        #endregion


        #region Properties

        [JsonIgnore]
        public IEnumerator<IAction> Actions
        {
            get { return actions.Values.GetEnumerator(); }
        }
        
        public bool ContinuousActions { get; protected set; }

        protected IActionFactory ActionFactory { get; set; }

        #endregion


        #region Events

        public event OnStartedPerformingActionsHandler OnStartedPerformingActions;

        public event OnStoppedPerformingActionsHandler OnStoppedPerformingActions;

        public event DestroyedHandler OnDestroyed;

        #endregion


        #region Constructors

        public ActionComponent()
        {
            actions = new Dictionary<string, IAction>();
            actionsToRemoveNextFrame = new List<string>(4);
        }

        public ActionComponent(ActionComponent old)
            :base(old)
        {
            this.actions = new Dictionary<string, IAction>();
            actionsToRemoveNextFrame = new List<string>(4);
            foreach (var action in old.actions.Values)
            {
                var newAction = action.DeepClone();
                newAction.Parent = this;
                this.actions.Add(action.Name, newAction);
            }
        }

        #endregion


        public override void Initialize()
        {
            foreach (var action in actions.Values)
                action.Initialize();

            base.Initialize();
        }

        public override void PostInitialize()
        {
            foreach (var action in actions.Values)
                action.PostInitialize();

            base.PostInitialize();
        }

        public bool StartPerformingAction(IGameObject target, string actionName, IActionData data = null, bool continuous = false)
        {
            bool started = false;

            if (actionName != null && actions.ContainsKey(actionName))
            {
                actions[actionName].Execute(target, data, continuous);
                started = true;
            }

            return started;
        }

        public void StopPerformingAction(string actionName)
        {
            if (actionName != null && actions.ContainsKey(actionName))
            {
                actions[actionName].Stop();
            }
        }

        public void PausePerformingAction(string actionName)
        {
            if (actionName != null && actions.ContainsKey(actionName))
            {
                actions[actionName].Pause();
            }
        }

        public void ResumePerformingAction(string actionName)
        {
            if (actionName != null && actions.ContainsKey(actionName))
            {
                actions[actionName].Resume();
            }
        }

        public void StopPerfomingActions()
        {
            foreach (var action in actions.Values)
            {
                action.Stop();
            }
        }

        public void PausePerformingActions()
        {
            foreach (var action in actions.Values)
            {
                action.Pause();
            }
        }

        public void ResumePerformingActions()
        {
            foreach (var action in actions.Values)
            {
                action.Resume();
            }
        }

        public void AddAction(IAction action, bool replace = false, bool keepStateIfReplacing = false)
        {
            if (actions.ContainsKey(action.Name) && replace)
            {
                actions.Remove(action.Name);
            }
        }

        public void RemoveAction(string actionName)
        {
            if (actions.ContainsKey(actionName))
            {
                actionsToRemoveNextFrame.Add(actionName);
            }
        }


        public override void Update(GameTime gameTime)
        {
            foreach (var actionName in actionsToRemoveNextFrame)
                actions.Remove(actionName);

            actionsToRemoveNextFrame.Clear();

            foreach (var action in actions.Values)
            {
                action.Update(gameTime);
            }
        }

        public override IBaseComponent DeepClone()
        {
            return new ActionComponent(this);
        }

        public override void Deserialize(XElement element)
        {
            ActionFactory = GameServiceManager.GetService<IActionFactory>();

            foreach (var actionElement in element.Element("actions").Elements("action"))
            {
                var action = ActionFactory.GenerateAction(actionElement);

                if (action == null) continue;

                action.Parent = this;
                actions.Add(action.Name, action);
            }
        }
    }
}
